using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using MinesweeperPlus.Scripts.Abstractions.Interfaces;
using MinesweeperPlus.Scripts.Abstractions.Enums;
using MinesweeperPlus.Scripts.Configs;
using MinesweeperPlus.Scripts.UI;
using MinesweeperPlus.Scripts.Utils;

namespace MinesweeperPlus.Scripts.Objects
{
    public sealed class UI : IGameObject
    {
        #region Constants
        private const string COLOR_SCHEME_CONFIG_KEY_NAME = "ColorSchemeConfig";

        private const string POPUP_MENU_TEXT = "Menu";
        private const string POPUP_NOT_MONEY_TEXT = "Not enough money!";
        private const string POPUP_BUY_TEXT_PART_1 = "Color scheme";
        private const string POPUP_BUY_TEXT_PART_2 = "Do you want to buy this?";
        private const string POPUP_GAME_EXIT_TEXT = "Do you want to exit?";
        private const string POPUP_PLAYFIELD_EXIT_TEXT = "Do you want to exit?";
        private const string POPUP_NOT_IMPLEMENTED_ERROR_TEXT = "not implemented!";

        private const float MIN_CAMERA_ZOOM = 1;
        private const int LEVEL_MARGINS = 1;

        private const int MAX_TIMER_VALUE = 3600;
        #endregion

        #region Variables
        private UIConfig _config;

        private GameObject _gameObject;
        private bool _isActive;
        private ColorSchemeConfig[] _colorSchemeConfigs => _config.ColorSchemeConfigs;
        private ColorSchemeConfig _colorSchemeConfig;
        private List<UIElement> _colorizableUIElements;
        private Vector3 _startCameraPosition;
        private float _startCameraZoom;
        private int _endTime;
        
        private PlayerCamera _playerCamera;
        private Playfield _playfield;
        private Timer _timer;
        private GameData _gameData;
        private IAds _interstitialAds;
        private IAds _rewardedAds;

        private UIDocument _uiDocument;
        private VisualElement _root;
        private VisualElement _uiPanel;
        private VisualElement _currentCenterPanel;
        private VisualElement _previousCenterPanel;
        private ColorSchemePanel _currentColorSchemePanel;

        private TopPanel _topPanel;
        private BottomPanel _bottomPanel;
        private MainPanel _mainPanel;
        private LevelsPanel _levelsPanel;
        private PlayfieldPanel _playfieldPanel;
        private ShopPanel _shopPanel;

        #endregion
        #region Properties
        private ColorSchemeConfig ColorSchemeConfig
        {
            get => _colorSchemeConfig;
            set
            {
                if (_colorSchemeConfig == value)
                    return;

                _colorSchemeConfig = value;

                _playerCamera.BackgroundColor = ColorSchemeConfig.CameraBackgroundColor;
                foreach (var uiElement in _colorizableUIElements)
                    uiElement.ColorSchemeConfig = ColorSchemeConfig;
            }
        }

        public Transform Transform { get; private set; }
        public bool IsActive 
        { 
            get => _isActive;
            set
            {
                if (_isActive == value)
                    return;

                _isActive = value;
                _gameObject.SetActive(_isActive);
            }
        }
        #endregion

        #region UI Methods
        public UI(
            UIConfig config, 
            PlayerCamera playerCamera,
            Playfield playfield, 
            Timer timer, 
            GameData gameData,
            IAds interstitialAds,
            IAds rewardedAds)
        {
            _config = config;
            _playerCamera = playerCamera;
            _uiDocument = GameObject.FindObjectOfType<UIDocument>();
            _root = _uiDocument.rootVisualElement;
            _uiPanel = _root.Q<SafeArea>();
            _gameObject = _uiDocument.gameObject;
            _colorizableUIElements = new();
            Transform = _uiDocument.transform;
            _playfield = playfield;
            _timer = timer;
            _gameData = gameData;
            _interstitialAds = interstitialAds;
            _rewardedAds = rewardedAds;

            InitTopPanel();
            InitMainPanel();
            InitLevelsPanel();
            InitPlayfieldPanel();
            InitBottomPanel();
            InitShopPanel();
        }

        public void SetCenterPanel(VisualElement panel, VisualElement previousPanel = null)
        {
            if (panel == _currentCenterPanel)
                return;
            
            _currentCenterPanel.SetDisplay(false);
            _previousCenterPanel = previousPanel ?? _currentCenterPanel;
            _currentCenterPanel = panel;
            _currentCenterPanel.SetDisplay(true);
        }

        public void Destroy()
        {
            GameObject.Destroy(_gameObject);
        }
        #endregion

        #region Other Methods
        private T Q<T>() where T : VisualElement =>
            _uiPanel.Q<T>();
    
        private void InitTopPanel()
        {
            _topPanel = Q<TopPanel>();
            _colorizableUIElements.Add(_topPanel);
            _topPanel.OnBackButtonClick += () =>
            {
                if (_currentCenterPanel is MainPanel)
                    Application.Quit();
                else
                    SetCenterPanel(_mainPanel);
            };
            _topPanel.OnToolButtonClick += (toolType) => _playfield.ToolType = toolType;
            _topPanel.OnMenuButtonClick += () => CreateMenuPopupPanel();
            
            _playfield.OnFirstCellOpen += _timer.Start;
            _playfield.OnCreateLevel += () => 
            {
                _topPanel.ShowToolButton();
                _topPanel.SetToolButtonType(PlayfieldToolType.Shovel);
            };
            _playfield.OnClearLevel += () => 
            {
                _topPanel.ShowBackButton();
                _topPanel.ShowMenuButton();
                _timer.Stop();
                _timer.Reset();
            };
            _playfield.OnEndPlay += (_) => _topPanel.HideAllButtons();
        
            _timer.OnTick += (value) =>
            {
                _topPanel.SetTimerTextFromSeconds(value);
                if (value >= MAX_TIMER_VALUE)
                    _playfield.EndPlay(false);
                
                _endTime = value;
            };
        }
        private void InitMainPanel()
        {
            _mainPanel = Q<MainPanel>();
            _colorizableUIElements.Add(_mainPanel);
            _mainPanel.OnPlayButtonClick += () => SetCenterPanel(_levelsPanel);
            _mainPanel.OnShopButtonClick += () => SetCenterPanel(_shopPanel);

            _currentCenterPanel = _mainPanel;
        }
        private void InitLevelsPanel()
        {
            _levelsPanel = Q<LevelsPanel>();

            foreach (var level in _playfield.Levels)
            {
                var levelPanel = new LevelPanel(_config.IconsConfig, _config.UIStyleSheet);
                levelPanel.SetLevelConfig(level.Config);
                levelPanel.OnButtonClick += () => 
                {
                    SetCenterPanel(_playfieldPanel);
                    _playfield.CreateLevel(level);
                    UpdateCameraPosition();
                };
                _levelsPanel.Add(levelPanel);

                _colorizableUIElements.Add(levelPanel);
            }
        }
        private void InitPlayfieldPanel()
        {
            _playfieldPanel = Q<PlayfieldPanel>();
            _colorizableUIElements.Add(_playfieldPanel);
            _playfieldPanel.OnBackButtonClick += () => 
            {
                SetCenterPanel(_levelsPanel, _mainPanel);
                _playfield.ClearCurrentLevel();
            };
            _playfieldPanel.OnVideoButtonClick += () => 
            {
                Action tmpAction = null;
                tmpAction = () =>
                {
                    _gameData.Money += _playfield.CurrentLevel.Config.NumberOfMines;
                    _playfieldPanel.HideVideoButton();

                    _rewardedAds.OnAdsShowComplete -= tmpAction;
                };
                _rewardedAds.OnAdsShowComplete += tmpAction;

                _rewardedAds.ShowAds();
            };
            _playfieldPanel.OnRestartButtonClick += () =>
            {
                _playfield.ResetCurrentLevel();
                _playerCamera.Position = _startCameraPosition;
            };

            _playfield.OnEndPlay += (endPlayType) => 
            {
                _timer.Stop();
                switch (endPlayType)
                {
                    case EndPlayType.Win:
                        int numberOfStars = 0;
                        var timeValuesForRemoveStar = 
                            _playfield.CurrentLevel.Config.TimeValuesForRemoveStar;
                        foreach (var time in timeValuesForRemoveStar)
                            if (_endTime >= time)
                                numberOfStars++;
                        
                        _playfieldPanel.ShowVideoButton();
                        _playfieldPanel.ShowWinPanel(timeValuesForRemoveStar.Length - numberOfStars);
                        break;
                    case EndPlayType.Lose:
                        _playfieldPanel.HideVideoButton();
                        _playfieldPanel.ShowLosePanel();
                        break;
                }
            };
        }
        private void InitShopPanel()
        {
            _shopPanel = Q<ShopPanel>();
            _shopPanel.OnColorSchemePanelClick += (colorSchemePanel) =>
            {
                if (colorSchemePanel.IsSelected)
                    return;

                if (colorSchemePanel.IsBought)
                    SelectColorSchemePanel(colorSchemePanel);
                else 
                {
                    var colorSchemeConfig = colorSchemePanel.GetColorSchemeConfig();
                    if (_gameData.Money - colorSchemeConfig.Price < 0)
                        CreateMessagePopupPanel(POPUP_NOT_MONEY_TEXT);
                    else
                    {
                        var buyPopup = CreatePopupPanel(
                            POPUP_BUY_TEXT_PART_1 + 
                            $" \'{colorSchemeConfig.Name}\'\n" + 
                            POPUP_BUY_TEXT_PART_2);
                        buyPopup.SetPanelType(PopupPanelType.YesOrNo);
                        buyPopup.OnButtonClick += (popupButtonType) =>
                        {
                            if (popupButtonType == PopupButtonType.Confirm)
                            {
                                _gameData.Money -= colorSchemeConfig.Price;
                                colorSchemePanel.IsBought = true;
                                SelectColorSchemePanel(colorSchemePanel);
                                _interstitialAds.ShowAds();
                            }
                            buyPopup.Close();
                        };
                    }
                }
            };

            foreach (var colorSchemeConfig in _colorSchemeConfigs)
                _shopPanel.AddColorSchemePanel(colorSchemeConfig);

            _currentColorSchemePanel = _shopPanel.ColorSchemePanels
                .Find(csc => csc.GetColorSchemeConfig().Name == _gameData.SelectedColorSchemeName);
            _currentColorSchemePanel.IsSelected = true;

            ColorSchemeConfig = _currentColorSchemePanel.GetColorSchemeConfig();
        }
        private void InitBottomPanel()
        {
            _bottomPanel = Q<BottomPanel>();
            _colorizableUIElements.Add(_bottomPanel);
            UpdateAndShowMoneyPanel();

            _gameData.OnMoneyValueChange += _bottomPanel.SetCountTextFromNumber;

            _playfield.OnNumberOfUnsetFlagsChange += _bottomPanel.SetCountTextFromNumber;
            _playfield.OnCreateLevel += () => _bottomPanel.SetPanelType(BottomPanelType.Flags);
            _playfield.OnClearLevel += UpdateAndShowMoneyPanel;
            _playfield.OnEndPlay += (_) => UpdateAndShowMoneyPanel();
        }

        private PopupPanel CreatePopupPanel(
            string text = "",
            params PopupButtonType[] buttonTypes)
        {
            var popupPanel = new PopupPanel(
                _config.IconsConfig, 
                _config.UIStyleSheet,
                ColorSchemeConfig);
            _uiPanel.Add(popupPanel);
            popupPanel.SetPopupText(text);
            popupPanel.ShowButtons(buttonTypes);
            return popupPanel;
        }
        
        private PopupPanel CreateMessagePopupPanel(string text)
        {
            var messagePopup = CreatePopupPanel(text);;
            messagePopup.SetPanelType(PopupPanelType.Message);
            return messagePopup;
        }

        private PopupPanel CreateMenuPopupPanel()
        {
            if (_currentCenterPanel is PlayfieldPanel)
                return CreatePlayfieldMenuPopupPanel();

            var popupMenu = CreatePopupPanel(
                POPUP_MENU_TEXT,
                PopupButtonType.Close,
                PopupButtonType.Info);
            popupMenu.OnButtonClick += (buttonType) =>
            {
                switch (buttonType)
                {
                    case PopupButtonType.Info:
                        CreateNotImplementedErrorPopupPanel("infoPanel");
                        popupMenu.Close();
                        break;
                }
            };
            return popupMenu;
        }
        private PopupPanel CreatePlayfieldMenuPopupPanel()
        {
            PausePlay();
            var popupMenu = CreatePopupPanel(
                POPUP_MENU_TEXT,
                PopupButtonType.Close,
                PopupButtonType.Back,
                PopupButtonType.Hints,
                PopupButtonType.Restart);
            popupMenu.OnButtonClick += (buttonType) =>
            {
                switch (buttonType)
                {
                    case PopupButtonType.Close:
                        ResumePlay();
                        break;
                    case PopupButtonType.Back:
                        var popupExit = CreatePopupPanel(
                            POPUP_PLAYFIELD_EXIT_TEXT,
                            PopupButtonType.Confirm,
                            PopupButtonType.Cancel);
                        popupExit.OnButtonClick += (buttonType) =>
                        {
                            if (buttonType == PopupButtonType.Confirm)
                            {
                                _playfield.ClearCurrentLevel();
                                SetCenterPanel(_levelsPanel, _mainPanel);
                                popupMenu.Close();
                            }
                            popupExit.Close();
                        };
                        break;
                    case PopupButtonType.Hints:
                        Action tmpAction = null;
                        tmpAction = () =>
                        {
                            _playfield.CurrentLevel.OpenRandomNonMinedCell();
                            popupMenu.Close();
                            ResumePlay();

                            _rewardedAds.OnAdsShowComplete -= tmpAction;
                        };
                        _rewardedAds.OnAdsShowComplete += tmpAction;

                        _rewardedAds.ShowAds();
                        break;
                    case PopupButtonType.Restart:
                        _playfield.ResetCurrentLevel();
                        _playerCamera.Position = _startCameraPosition;
                        popupMenu.Close();
                        _timer.Reset();
                        break;
                }
            };
            return popupMenu;
        }

        private PopupPanel CreateExitPopupPanel(Action onConfirm)
        {
            var popupPanel = CreatePopupPanel(POPUP_GAME_EXIT_TEXT);
            popupPanel.SetPanelType(PopupPanelType.YesOrNo);
            popupPanel.OnButtonClick += (buttonType) =>
            {
                if (buttonType == PopupButtonType.Confirm)
                    onConfirm();
                popupPanel.Close();
            };
            return popupPanel;
        }

        private PopupPanel CreateNotImplementedErrorPopupPanel(string obejctName) 
        {
            var popupPanel = CreatePopupPanel($"\'{obejctName}\'\n{POPUP_NOT_IMPLEMENTED_ERROR_TEXT}");
            popupPanel.SetPanelType(PopupPanelType.Error);
            return popupPanel;
        }

        private void PausePlay()
        {
            _playfield.PauseInput();
            _timer.Stop();
        }
        private void ResumePlay()
        {
            _playfield.ResumeInput();
            _timer.Start();
        }
        
        private void UpdateAndShowMoneyPanel()
        {
            _bottomPanel.SetPanelType(BottomPanelType.Money);
            _bottomPanel.SetCountTextFromNumber(_gameData.Money);
        }
        
        private void UpdateCameraPosition()
        {
            var levelConfig = _playfield.CurrentLevel.Config;
            var levelWidth = levelConfig.Width;
            var levelHeight = levelConfig.Height;

            var rootRect = _uiPanel.parent.worldBound;
            var topRect = _topPanel.worldBound;
            var levelsRect = _levelsPanel.worldBound;
            var bottomRect = _bottomPanel.worldBound;

            _startCameraZoom = Mathf.Max(
                rootRect.height / levelsRect.height * (levelHeight / 2f + LEVEL_MARGINS),
                rootRect.height / rootRect.width * (levelWidth / 2f + LEVEL_MARGINS));
            _playerCamera.ZoomRange = new Vector2(MIN_CAMERA_ZOOM, _startCameraZoom * 2);
            _playerCamera.Zoom = _startCameraZoom;
            
            _startCameraPosition = new Vector3(
                0, 
                (levelsRect.center - rootRect.center).y * 2 * _startCameraZoom / rootRect.height,
                -1);
            _playerCamera.PositionRange = new Rect(
                -levelWidth, 
                -levelHeight,
                levelWidth * 2,
                levelHeight * 2);
            _playerCamera.Position = _startCameraPosition;
            
        }
        
        private void SelectColorSchemePanel(ColorSchemePanel colorSchemePanel)
        {
            _currentColorSchemePanel.IsSelected = false;
            colorSchemePanel.IsSelected = true;
            ColorSchemeConfig = colorSchemePanel.GetColorSchemeConfig();
            _currentColorSchemePanel = colorSchemePanel;

            _gameData.SelectedColorSchemeName = ColorSchemeConfig.Name;
        }
        #endregion
    }
}
